/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.ericsson.nfc.signing.algorithm;

import com.ericsson.nfc.signing.bc.crypto.signers.EcdsaSigValue;
import com.ericsson.nfc.signing.record.CertificateBytes;
import com.ericsson.nfc.signing.record.SignatureField;
import com.ericsson.nfc.signing.record.SignatureRecord;
import com.ericsson.nfc.signing.record.URIRecord;
import java.io.IOException;
import java.math.BigInteger;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.sec.ECPrivateKeyStructure;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.util.encoders.Hex;

//#ifdef MyJSR257
//# import my.javax.microedition.contactless.ndef.NDEFRecordType;
//#else
import javax.microedition.contactless.ndef.NDEFRecordType;
//#endif

/**
 *
 * @author emarkki
 */
public class ECDSASigner extends Signer {

    private static final DERObjectIdentifier ALG_ID = new DERObjectIdentifier("1.2.840.10045.2.1");
    
    public ECDSASigner(PrivateKey privateKey, int certificateType, CertificateBytes[] chain, URIRecord certURI) {
        this(privateKey, certificateType, chain, certURI, SignatureRecord.RECORD_TYPE);
    }

    public ECDSASigner(PrivateKey privateKey, int certificateType, CertificateBytes[] chain, URIRecord certURI, NDEFRecordType signatureRecordType) {
        super(SignatureField.ECDSA_P_192_SHA1, privateKey, certificateType, chain, certURI, signatureRecordType);
                
        if(!ALG_ID.equals(privateKey.getPrivateKeyInfo().getAlgorithmId().getObjectId())) {
            throw new WrongPrivateKeyAlgorithm("Private key algorithm is not ECDSA");
        }
    }
    
    public byte[] sign(byte[] coveredBytes) throws SignatureException, DataLengthException {
        try {
            BigInteger[] rs = signUsingECDSA(coveredBytes, getPrivateKey());
            return encodeAsInX962(rs[0], rs[1]);
        } catch (CryptoException ex) {
            ex.printStackTrace();
            throw new SignatureException(ex);
        }
    }

    static BigInteger[] signUsingECDSA(byte[] coveredBytes, PrivateKey privKey) throws DataLengthException, CryptoException {
        
        PrivateKeyInfo privKeyInfo = privKey.getPrivateKeyInfo();
        ECPrivateKeyStructure structure = new ECPrivateKeyStructure(ASN1Sequence.getInstance(privKeyInfo.getPrivateKey()));

        // Domain model variables
        BigInteger q = new BigInteger("6277101735386680763835789423207666416083908700390324961279");
        BigInteger a = new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16);
        BigInteger b = new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16);
        BigInteger n = new BigInteger("6277101735386680763835789423176059013767194773182842284081");
        byte[] G = Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012");

        ECCurve.Fp curve = new ECCurve.Fp(q, a, b);
        ECDomainParameters params = new ECDomainParameters(curve, curve.decodePoint(G), n);
        ECKeyParameters privParameters = new ECPrivateKeyParameters(structure.getKey(), params);
                
        Digest digest = DigestFactory.getDigest("SHA-1");
        digest.update(coveredBytes, 0, coveredBytes.length);
        byte[] digestBytes = new byte[digest.getDigestSize()];
        digest.doFinal(digestBytes, 0);

        // Bouncy castle signer
        org.bouncycastle.crypto.signers.ECDSASigner signer = new org.bouncycastle.crypto.signers.ECDSASigner();
        signer.init(true, privParameters);
        
        // Signature
        return signer.generateSignature(digestBytes);
    }
    
    static byte[] encodeAsInX962(BigInteger r, BigInteger s) {
        return new EcdsaSigValue(r, s).getDEREncoded();
    }
    
    static BigInteger[] decodeAsInX962(byte[] signature) throws IOException {
        ASN1InputStream aIn = new ASN1InputStream(signature);
        DERObject o = aIn.readObject();

        ASN1Sequence encodedSeq = (ASN1Sequence) o;
        EcdsaSigValue ecdsaSigValue = new EcdsaSigValue(encodedSeq);
        
        return new BigInteger[]{ecdsaSigValue.getR(), ecdsaSigValue.getS()};
    }
    
//    static byte[] encodeAsInCVC(BigInteger r, BigInteger s) {
//        
//    }
//    
//    static BigInteger[] decodeAsInCVC(byte[] signature) throws IOException {
//        
//    }
}
